﻿using System;
using System.Text;

namespace TaiwuModFramework.Common.Abstract {
    public abstract class ALogger {

        private class EmptyLogger : ALogger {
            protected override void WriteToLogPath(LEVEL level, string msg) {
                
            }
        }
        public static  ALogger emptyLogger { get; private set; } = new EmptyLogger();

        public enum LEVEL : int{
            TRACE, DEBUG, INFO, WARN, ERROR, FATAL
        }


        public LEVEL Level { get; set; }
        public string Name { get; protected set; }
        public ALogWriter LogWriter { get; protected set; } = null;
        public string LogPath { get {
                return LogWriter != null ? LogWriter.LogPath : string.Empty;
            } }

        public void SetLogWriter(ALogWriter logWriter) {
            LogWriter = logWriter;
        }

        /// <summary>
        /// 向文件写入指定级别的日志
        /// </summary>
        /// <param name="level">日志级别</param>
        /// <param name="msg">日志</param>
        /// <param name="keepHeader"></param>
        protected abstract void WriteToLogPath(LEVEL level, string msg);

        private void CheckAndWriteInfo(LEVEL level, Func<string> func) {
            if(!CheckInfoVisible(level)) return;
            WriteToLogPath(level, func());
        }
        private void CheckAndWriteInfo(LEVEL level, string msg, params object[] args) {
            if(!CheckInfoVisible(level)) return;
            if(args == null || args.Length == 0) {
                WriteToLogPath(level, msg);
            } else {
                WriteToLogPath(level, string.Format(msg, args));
            }
        }
        private void CheckAndWriteInfo(LEVEL level, object obj) {
            if(!CheckInfoVisible(level)) return;
            if(obj != null) {
                WriteToLogPath(level, obj.ToString());
            }
        }

        public void Debug(object obj) {
            CheckAndWriteInfo(LEVEL.DEBUG, obj);
        }
        public void Debug(string msg, params object[] args) {
            CheckAndWriteInfo(LEVEL.DEBUG, msg, args);
        }

        public void Debug(Func<string> option) {
            CheckAndWriteInfo(LEVEL.DEBUG, option);
        }
        public void Error(object obj) {
            CheckAndWriteInfo(LEVEL.ERROR, obj);
        }
        public void Error(string msg, params object[] args) {
            CheckAndWriteInfo(LEVEL.ERROR, msg, args);
        }

        public void Error(Func<string> option) {
            CheckAndWriteInfo(LEVEL.ERROR, option);
        }
        public void Fatal(object obj) {
            CheckAndWriteInfo(LEVEL.FATAL, obj);
        }
        public void Fatal(string msg, params object[] args) {
            CheckAndWriteInfo(LEVEL.FATAL, msg, args);
        }

        public void Fatal(Func<string> option) {
            CheckAndWriteInfo(LEVEL.FATAL, option);
        }
        public void Info(object obj) {
            CheckAndWriteInfo(LEVEL.INFO, obj);
        }
        public void Info(string msg, params object[] args) {
            CheckAndWriteInfo(LEVEL.INFO, msg, args);
        }

        public void Info(Func<string> option) {
            CheckAndWriteInfo(LEVEL.INFO, option);
        }
        public void Trace(object obj) {
            CheckAndWriteInfo(LEVEL.TRACE, obj);
        }
        public void Trace(string msg, params object[] args) {
            CheckAndWriteInfo(LEVEL.TRACE, msg, args);
        }

        public void Trace(Func<string> option) {
            CheckAndWriteInfo(LEVEL.TRACE, option);
        }
        public void Warn(object obj) {
            CheckAndWriteInfo(LEVEL.WARN, obj);
        }
        public void Warn(string msg, params object[] args) {
            CheckAndWriteInfo(LEVEL.WARN, msg, args);
        }

        public void Warn(Func<string> option) {
            CheckAndWriteInfo(LEVEL.WARN, option);
        }


        public void Trace(Exception ex) {
            Trace(GetExceptionInfo(ex));
        }

        public void Debug(Exception ex) {
            Debug(GetExceptionInfo(ex));
        }

        public void Info(Exception ex) {
            Info(GetExceptionInfo(ex));
        }

        public void Warn(Exception ex) {
            Warn(GetExceptionInfo(ex));
        }

        public void Error(Exception ex) {
            Error(GetExceptionInfo(ex));
        }

        public void Fatal(Exception ex) {
            Fatal(GetExceptionInfo(ex));
        }

        public void Log(LEVEL level, string msg) {
            if(!CheckInfoVisible(level)) return;
            switch(level) {
                case LEVEL.TRACE:
                    Trace(msg);
                    break;
                case LEVEL.DEBUG:
                    Debug(msg);
                    break;
                case LEVEL.INFO:
                    Info(msg);
                    break;
                case LEVEL.WARN:
                    Warn(msg);
                    break;
                case LEVEL.ERROR:
                    Error(msg);
                    break;
                case LEVEL.FATAL:
                    Fatal(msg);
                    break;
            };
        }
        public void Log(LEVEL level, Exception e) {
            if(CheckInfoVisible(level)) Log(level, GetExceptionInfo(e));
        }
        public void Log(LEVEL level, Func<string> option) {
            if(CheckInfoVisible(level)) Log(level, option());
        }
        public void SetLevel(int level) {
            Level = LevelParse(level);
        }
        public void SetLevel(LEVEL level) {
            Level = level;
        }

        public bool CheckInfoVisible(LEVEL level) {
            return Level <= level;
        }
        public static string GetExceptionInfo(Exception ex) {
            StringBuilder sb = new StringBuilder();
            sb.Append(string.Format(
                "捕获到{0}异常\n异常信息:{1}\n{2}",
                ex.GetType().FullName,
                ex.Message,
                ex.StackTrace
                ));

            return sb.ToString();
        }


        public static LEVEL LevelParse(int level) {
            if(level <= (int)LEVEL.TRACE) {
                return LEVEL.TRACE;
            } else if(level >= (int)LEVEL.FATAL) {
                return LEVEL.FATAL;
            } else {
                return (LEVEL)level;
            }
        }
    }
}
